/*
 *   This file is part of YARS.
 *
 *   Foobar is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   YARS is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Lesser General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public License
 *   along with YARS.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *  Creator: Tom Wadzinski
 * 
 * 
 */

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;
using System.Globalization;
using System.Web;
using LitJson;

namespace MC_CSPlugin
{

    public class JsonServer
    {
        private MainInterface mainInterface;

        public JsonServer(MainInterface main, string[] prefixes)
        {
            mainInterface = main;
            if (!System.Net.HttpListener.IsSupported)
            {
                mainInterface.addUserInfoText(
                    "Windows XP SP2, Server 2003, or higher is required to " +
                    "use the HttpListener class.");
                return;
            }
            // URI prefixes are required,
            if (prefixes == null || prefixes.Length == 0)
                throw new ArgumentException("prefixes");

            // Create a listener and add the prefixes.
            System.Net.HttpListener listener = new System.Net.HttpListener();
            foreach (string s in prefixes)
            {
                mainInterface.addUserInfoText("JSON Server listening at " + s);
                listener.Prefixes.Add(s);
            }

            try
            {
                // Start the listener to begin listening for requests.
                listener.Start();
                for (int i = 0; i < 10; i++)
                {

                    IAsyncResult result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);

                }

            }
            catch (HttpListenerException ex)
            {
                mainInterface.addUserInfoText("Error: " + ex.Message + "\n" + ex.StackTrace);

            }
        }

        public void ListenerCallback(IAsyncResult result)
        {
            HttpListener listener = (HttpListener)result.AsyncState;
            try
            {
                HttpListenerContext context = listener.EndGetContext(result);
                HttpListenerRequest request = context.Request;
                HttpListenerResponse response = context.Response;
                response.ContentType = "text/json";

                System.IO.Stream output = response.OutputStream;

                WriteString(processCommand(request), output);

                context.Response.OutputStream.Close();
                context.Response.Close();
            }
            catch (Exception e)
            {
                mainInterface.addUserInfoText("Error: " + e.Message + "\n" + e.StackTrace);
            }
            finally
            {
                listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
            }
        }

        private String processCommand(HttpListenerRequest request)
        {
            StringBuilder body = new StringBuilder();
            JsonWriter json = new JsonWriter(body);

            mainInterface.addUserInfoText("Request: " + request.RawUrl);
            string command = request.QueryString.Get("command");
            if (command == null)
            {
                command = "show";
            }

            switch (command)
            {
                case "play-item":
                    commandPlayItem(request, json);
                    break;
                case "add-end":
                    commandAddEnd(request, json);
                    break;
                case "show":
                    commandShow(request, json);
                    break;
                case "info":
                    commandInfo(request, json);
                    break;
                case "now-playing":
                    commandNowPlaying(request, json);
                    break;
                case "show-all":
                    commandShowAll(request, json);
                    break;
                case "play-pause":
                    GetRequestedZone(request).GetPlayback().Pause();
                    break;
                case "next":
                    GetRequestedZone(request).GetPlayback().Next();
                    break;
                case "previous":
                    GetRequestedZone(request).GetPlayback().Previous();
                    break;
                case "forward":
                    commandForward(request);
                    break;
                case "rewind":
                    commandRewind(request);
                    break;
                case "get-zones":
                    commandGetZones(json);
                    break;
                case "set-now-playing-index":
                    commandSetNowPlayingIndex(request);

                    break;
                case "volume-up":
                    commandVolumeUp(request);
                    break;
                case "volume-down":
                    commandVolumeDown(request);
                    break;
                case "rate":
                    commandRate(request, json);
                    break;
                //case "play-in-squeeze":
                //    commandPlayInSqueeze(request, json);
                //    break;
            }
        		
	       

            
            return body.ToString();
        }

        private void commandForward(HttpListenerRequest request)
        {
            if (GetRequestedZone(request).GetPlayback().IsVideo())
            {
                int amount = 30;
                if (GetRequestedZone(request).GetPlayback().Duration - GetRequestedZone(request).GetPlayback().Position > amount)
                {
                    GetRequestedZone(request).GetPlayback().Position += amount;
                }
            }
            else
            {
                GetRequestedZone(request).GetPlayback().FastForward();
            }
        }

        private void commandRewind(HttpListenerRequest request)
        {
            if (GetRequestedZone(request).GetPlayback().IsVideo())
            {
                int amount = 7;
                if (GetRequestedZone(request).GetPlayback().Position > amount)
                {
                    GetRequestedZone(request).GetPlayback().Position -= 7;
                }
                else
                {
                    GetRequestedZone(request).GetPlayback().Position = 0;
                }
            }
            else
            {
                GetRequestedZone(request).GetPlayback().Rewind();
            }
        }

        private void commandVolumeDown(HttpListenerRequest request)
        {
            GetRequestedZone(request).GetMixer().Volume -= 1;
        }

        private void commandVolumeUp(HttpListenerRequest request)
        {
            GetRequestedZone(request).GetMixer().Volume += 1;
        }

        private void commandSetNowPlayingIndex(HttpListenerRequest request)
        {
            string indexString = request.QueryString.Get("index");
            MediaCenter.IMJCurPlaylistAutomation nowPlaying = GetRequestedZone(request).GetCurPlaylist();

            if (GetRequestedZone(request).GetPlayback().State == MediaCenter.MJPlaybackStates.PLAYSTATE_PLAYING)
            {
                GetRequestedZone(request).GetPlayback().Stop();
            }
            int index = Int32.Parse(indexString) - 1;
            if (index >= 0 && index <= nowPlaying.GetNumberFiles())
            {
                nowPlaying.Position = index;
            }
            GetRequestedZone(request).GetPlayback().Play();
        }

        private void commandNowPlaying(HttpListenerRequest request, JsonWriter json)
        {
            MediaCenter.IMJCurPlaylistAutomation nowPlaying = GetRequestedZone(request).GetCurPlaylist();
            int filesCount = nowPlaying.GetNumberFiles();
            int currentlyPlayingIndex = nowPlaying.Position;

            json.WriteArrayStart();
            for (int i = 0; i < filesCount; i++)
            {
                MediaCenter.IMJFileAutomation file = nowPlaying.GetFile(i);
                AppendTrackInfo(json, file.Filename, file.Name, "" + file.Rating, (i == currentlyPlayingIndex ? true : false), file.AlbumArtist, file.Album );
            }
            json.WriteArrayEnd();
        }


        private void commandGetZones(JsonWriter json)
        {
            MediaCenter.IMJZonesAutomation zones = mainInterface.mcRef.GetZones();
            int zonesCount = zones.GetNumberZones();


            json.WriteArrayStart();

            for (int i = 0; i < zonesCount; i++)
            {

                json.WriteObjectStart();

                json.WritePropertyName("zone");
                json.Write(i);
                json.WritePropertyName("zoneName");
                json.Write(zones.GetZoneName(i));

                json.WriteObjectEnd();
            }
            json.WriteArrayEnd();
        }

        private MediaCenter.IMJZoneAutomation GetRequestedZone(HttpListenerRequest request)
        {
            MediaCenter.IMJZonesAutomation zones = mainInterface.mcRef.GetZones();
            string zone = request.QueryString.Get("zone");
            if (zone == null || zone == "")
            {
                return zones.GetZone(zones.GetActiveZone());
            }
            return zones.GetZone(Int32.Parse(zone));
        }

        private void commandRate(HttpListenerRequest request, JsonWriter json)
        {
            String filename = GetFilenameParam(request);
            MediaCenter.IMJFileAutomation file = mainInterface.mcRef.GetFile(filename);
            String rating = request.QueryString.Get("rating");
            file.Set("Rating", "unrated".Equals(rating) ? null : rating);
        }

        private void commandPlayItem(HttpListenerRequest request, JsonWriter json)
        {
            MediaCenter.IMJCurPlaylistAutomation nowPlaying = GetRequestedZone(request).GetCurPlaylist();
            nowPlaying.RemoveAllFiles();
            commandAddEnd(request, json);
            nowPlaying.Position = 0;
            GetRequestedZone(request).GetPlayback().Play();
        }

        private void commandAddEnd(HttpListenerRequest request, JsonWriter json)
        {
            MediaCenter.IMJCurPlaylistAutomation nowPlaying = GetRequestedZone(request).GetCurPlaylist();
            String filename = GetFilenameParam(request);
            if (filename != null)
            {
                nowPlaying.AddFile(filename, nowPlaying.GetNumberFiles());
            }
            else
            {
                string location = GetLocationParam(request);
                MediaCenter.IMJViewItemAutomation item = mainInterface.mcRef.GetViewItem(location);
                MediaCenter.IMJFilesAutomation files = item.GetFiles();
                for (int i = 0; i < files.GetNumberFiles(); i++)
                {
                    nowPlaying.AddFile(files.GetFile(i).Filename, nowPlaying.GetNumberFiles());
                }
            }
        }
        //private void commandPlayInSqueeze(HttpListenerRequest request, JsonWriter json)
        //{
        //    String file = request.QueryString.Get("file");
        //    String encodedString = file.Replace("\\", "/").Replace(" ", "%20");

        //    TcpClient MyClient = new TcpClient();
        //    MyClient.Connect("127.0.0.1", 9090);
        //    NetworkStream MyNetStream = MyClient.GetStream();

        //    if (MyNetStream.CanWrite && MyNetStream.CanRead)
        //    {

        //        // Does a simple write.
        //        Byte[] sendBytes = Encoding.ASCII.GetBytes("00:04:20:06:33:d0 playlist play file:///" + encodedString + "\n");
        //        MyNetStream.Write(sendBytes, 0, sendBytes.Length);

        //        // Reads the NetworkStream into a byte buffer.
        //        byte[] bytes = new byte[MyClient.ReceiveBufferSize];
        //        MyNetStream.Read(bytes, 0, (int)MyClient.ReceiveBufferSize);

        //        // Returns the data received from the host to the console.
        //        string returndata = Encoding.ASCII.GetString(bytes);
        //        mainInterface.addUserInfoText("This is what the host returned to you: " + returndata);

        //    }
        //    else if (!MyNetStream.CanRead)
        //    {
        //        mainInterface.addUserInfoText("You can not write data to this stream");
        //        MyClient.Close();
        //    }
        //    else if (!MyNetStream.CanWrite)
        //    {
        //        mainInterface.addUserInfoText("You can not read data from this stream");
        //        MyClient.Close();
        //    }

        //    //body.Append("Sent Song to SqueezeCenter");
        //}

        private void commandShowAll(HttpListenerRequest request, JsonWriter json)
        {
            string location = GetLocationParam(request); 
            if (location != null)
            {
                json.WriteArrayStart();
         
                MediaCenter.IMJViewItemAutomation item = mainInterface.mcRef.GetViewItem(location);
                MediaCenter.IMJFilesAutomation files = item.GetFiles();
                int filesCount = files.GetNumberFiles();
                for (int i = 0; i < filesCount; i++)
                {
                    appendTrackInfo(files, i, json, null);
                }

                json.WriteArrayEnd();
            }
        }

        private void commandInfo(HttpListenerRequest request, JsonWriter json)
        {
            String filename = GetFilenameParam(request);
            MediaCenter.IMJFieldsAutomation fields = mainInterface.mcRef.GetFields();
            int fieldsCount = fields.GetNumberFields();
            MediaCenter.IMJFileAutomation file = mainInterface.mcRef.GetFile(filename);

            json.WriteArrayStart();
            for (int i = 0; i < fieldsCount; i++)
            {
                MediaCenter.IMJFieldAutomation field = fields.GetField(i);
                String fieldName = field.GetName(false);
                String value = file.Get(fieldName, false);
                if (value != null && !value.Equals(""))
                {
                    json.WriteObjectStart();

                    json.WritePropertyName("field");
                    json.Write(fieldName);
                    json.WritePropertyName("value");
                    json.Write(value);
                    json.WritePropertyName("formattedValue");
                    json.Write(file.Get(fieldName, true));
                    json.WriteObjectEnd();

                }
            }
            json.WriteArrayEnd();

            //String[] fieldnames = request.QueryString.GetValues("field");
            //for (int i = 0; i < fieldnames.Length; i++)
            //{
            //    String value = file.Get(fieldnames[i], false);
            //    if (value != null)
            //    {
            //        body.Append(HttpUtility.HtmlEncode(fieldnames[i]) + " --- Value: " + HttpUtility.HtmlEncode(value) + "<br>");
            //    }
            //}
        }

        private String GetFilenameParam(HttpListenerRequest request)
        {
            return request.QueryString.Get("file");
        }

        private void commandShow(HttpListenerRequest request, JsonWriter json)
        {
            //json.PrettyPrint = true;
            string location = GetLocationParam(request);
            if (location == null)
            {
                location = "Media Library";
            }
            MediaCenter.IMJViewItemAutomation item = mainInterface.mcRef.GetViewItem(location);
            int itemsCount = item.GetNumberChildren();

            json.WriteArrayStart();
            if (itemsCount > 0)
            {
                for (int i = 0; i < itemsCount; i++)
                {
                    String childName = item.GetChildName(i);
                    json.WriteObjectStart();

                    json.WritePropertyName("text");
                    json.Write(childName);
                    json.WritePropertyName("location");
                    json.Write(DoubleUrlEncode(item.GetFullName() + "\\" + childName));
                    json.WritePropertyName("isFolder");
                    json.Write(true);

                    json.WriteObjectEnd();
                }
            }
            else
            {
                MediaCenter.IMJFilesAutomation files = item.GetFiles();
                int filesCount = files.GetNumberFiles();
                for (int i = 0; i < filesCount; i++)
                {
                    appendTrackInfo(files, i, json, null);
                }
            }
            json.WriteArrayEnd();
        }

        /*
         * Double encoded to insure  we are the only true decoders (second encode will always be against ascii)
         */
        private static string DoubleUrlEncode(String input)
        {
            return HttpUtility.UrlEncode(HttpUtility.UrlEncode(input));
        }

        private static string GetLocationParam(HttpListenerRequest request)
        {
            return HttpUtility.UrlDecode(request.QueryString.Get("location"));
        }

        private static void AppendJsonStringObject(JsonWriter json, String name, String value)
        {
            json.WriteObjectStart();
            json.WritePropertyName(name);
            json.Write(value);
            json.WriteObjectEnd();
        }

        /**
         * Note: Intentionally not using IMJFileAutomation object, was about two times slower then with Files.Get()
         */
        private void appendTrackInfo(MediaCenter.IMJFilesAutomation files, int index, JsonWriter json, Boolean? isNowPlayingTrack)
        {
            string filename = files.Get(index, "Filename");
            string name = files.Get(index, "Name");
            String rating = files.Get(index, "Rating");

            AppendTrackInfo(json, filename, name, rating, isNowPlayingTrack, null, null);

        }

        private static void AppendTrackInfo(JsonWriter json, string filename, string name, String rating, Boolean? isNowPlayingTrack, String artist, String album)
        {
            json.WriteObjectStart();

            json.WritePropertyName("text");
            json.Write(name);
            json.WritePropertyName("file");
            json.Write(HttpUtility.UrlEncode(filename));

            json.WritePropertyName("rating");
            if (rating.Equals("? stars", System.StringComparison.CurrentCultureIgnoreCase))
            {
                rating = "unrated";
            }
            json.Write(rating);

            json.WritePropertyName("isFolder");
            json.Write(false);

            if (isNowPlayingTrack.HasValue)
            {
                json.WritePropertyName("isNowPlayingTrack");
                json.Write(isNowPlayingTrack ?? false);

                json.WritePropertyName("name");
                json.Write(name);

                json.WritePropertyName("artist");
                json.Write(artist);

                json.WritePropertyName("album");
                json.Write(album);

            }
            json.WriteObjectEnd();
        }


        private void WriteString(String output, System.IO.Stream stream)
        {
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(output);
            stream.Write(bytes, 0, bytes.Length);
        }

        
    }
}